/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.diagram.usecase.editpart;

import java.util.ArrayList;

import org.eclipse.draw2d.IFigure;
import org.eclipse.draw2d.Label;
import org.eclipse.draw2d.PositionConstants;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.geometry.Point;
import org.eclipse.draw2d.geometry.Rectangle;
import org.eclipse.gef.EditPart;
import org.eclipse.gef.EditPolicy;
import org.eclipse.gef.GraphicalEditPart;
import org.eclipse.gef.Request;
import org.eclipse.gef.RequestConstants;
import org.eclipse.gef.palette.PaletteDrawer;
import org.eclipse.gef.tools.CellEditorLocator;
import org.eclipse.gef.tools.DirectEditManager;
import org.eclipse.uml2.uml.Association;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.ElementImport;
import org.eclipse.uml2.uml.Extend;
import org.eclipse.uml2.uml.Generalization;
import org.eclipse.uml2.uml.Include;
import org.eclipse.uml2.uml.UseCase;

import com.cea.papyrus.core.PapyrusMessages;
import com.cea.papyrus.core.PapyrusPlugin;
import com.cea.papyrus.core.directedit.ILabelControler;
import com.cea.papyrus.core.directedit.LabelCellEditorLocator;
import com.cea.papyrus.core.directedit.LabelControler;
import com.cea.papyrus.core.directedit.LabelDirectEditManager;
import com.cea.papyrus.core.directedit.PropertyAccessor;
import com.cea.papyrus.core.editor.ModelManager;
import com.cea.papyrus.core.editor.mouseoveraction.ActionHandleManager;
import com.cea.papyrus.core.editpart.ContainerWithSemanticEditPart;
import com.cea.papyrus.core.editpart.connection.AnchorMngr;
import com.cea.papyrus.core.editpart.connection.EllipseAnchorMngr;
import com.cea.papyrus.core.figures.IconFigure;
import com.cea.papyrus.core.figures.NodeFigure;
import com.cea.papyrus.core.helper.NodeHelper;
import com.cea.papyrus.core.utils.Debug;
import com.cea.papyrus.diagram.usecase.figure.BasicUseCaseClassifierFigure;
import com.cea.papyrus.diagram.usecase.figure.UseCaseClassifierFormFigure;
import com.cea.papyrus.diagram.usecase.figure.UseCaseEllipseFormFigure;
import com.cea.papyrus.diagram.usecase.figure.UseCaseEllipseFormNameOutsideFigure;
import com.cea.papyrus.diagram.usecase.figure.UseCaseEllipseFormWithExtensionPointsFigure;
import com.cea.papyrus.diagram.usecase.figure.UseCaseEllipseGeneralFigure;
import com.cea.papyrus.diagram.usecase.helper.NodeUseCaseHelper;
import com.cea.papyrus.diagraminterchange2.di2.GraphNode;
import com.cea.papyrus.diagraminterchange2.di2.Property;
import com.cea.papyrus.diagraminterchange2.di2.Uml1SemanticModelBridge;

/**
 * Controller for a Use Case
 */
public class UseCaseEditPart extends ContainerWithSemanticEditPart {

	/** Manager for editing classname */
	private DirectEditManager manager;

	/**
	 * Creates a new UseCaseEditPart.
	 * @param modelManager the model manager associated to the model
	 * @param graphNode the graph node representing the use case
	 */
	public UseCaseEditPart(GraphNode graphNode, ModelManager modelManager) {
		super(graphNode, modelManager);
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.editpart.GraphNodeWithSemanticEditPart#createFigure()
	 */
	@Override
	protected IFigure createFigure() {
		// Force initialization of the manager when figure has changed
		manager = null;
		// Try to get figure from GraphNodeWithSemanticEditPart
		IFigure fig = super.createFigure();
		if (fig != null) {
			return fig;
		}
		// Else
		if (!getGraphNode().getProperty().isEmpty()) {
			Property propertyAppearance = (Property) getGraphNode().getProperty(NodeHelper.NODE_FIGURE_APPEARANCE);
			if (propertyAppearance != null) {
				if (propertyAppearance.getValue().equals(NodeUseCaseHelper.USECASE_ELLIPSE_FORM)) {
					if (hasExtensionPoints()){
						return new UseCaseEllipseFormWithExtensionPointsFigure();
					} else {
						return new UseCaseEllipseFormFigure();
					}
				} else if (propertyAppearance.getValue().equals(NodeUseCaseHelper.USECASE_ELLIPSE_FORM_NAME_OUTSIDE)) {
					if (hasExtensionPoints()){
						return new UseCaseEllipseFormWithExtensionPointsFigure();
					} else {
						return new UseCaseEllipseFormNameOutsideFigure();
					}
				} else if (propertyAppearance.getValue().equals(NodeUseCaseHelper.USECASE_CLASSIFIER_FORM)) {
					return new UseCaseClassifierFormFigure();
				}
			}
		}
		// Else...
		return new UseCaseEllipseFormFigure();
	}

	/**
	 * Has extension points.
	 * @return <code>true</code>, if extension point graphNode list has children
	 */
	private boolean hasExtensionPoints() {
		GraphNode extensionPointList = (GraphNode) getGraphNode().getContained().get(0);	
		boolean showExtensionPoints = !(extensionPointList.getContained().isEmpty());
		return showExtensionPoints;
	}

	/**
	 * Modify ellipse form.
	 */
	public void modifyEllipseFormAppearance() {
		// Change de figure ellipse appearance property if necessary	
		if (!getGraphNode().getProperty().isEmpty()) {
			Property propertyAppearance = getGraphNode().getProperty(NodeHelper.NODE_FIGURE_APPEARANCE);
			if ( propertyAppearance != null ) {
				if ( propertyAppearance.getValue().equals(NodeUseCaseHelper.USECASE_ELLIPSE_FORM)
					|| propertyAppearance.getValue().equals(NodeUseCaseHelper.USECASE_ELLIPSE_FORM_NAME_OUTSIDE) ) {
					
					changeEditPartFigure();
					// Force node name refresh 
					getGraphNode().setBackgroundColor(getGraphNode().getBackgroundColor());
				} else {
					refreshVisuals();
				}
			}
		}
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.connections.editPart.AnchorableGraphNodeEditPart#createAnchorManager()
	 */
	@Override
	protected AnchorMngr createAnchorManager() {
		if (!getGraphNode().getProperty().isEmpty()) {
			Property propertyAppearance = getGraphNode().getProperty(NodeHelper.NODE_FIGURE_APPEARANCE);
			if ( propertyAppearance != null ) {
				if ( propertyAppearance.getValue().equals(NodeUseCaseHelper.USECASE_ELLIPSE_FORM)
					|| propertyAppearance.getValue().equals(NodeUseCaseHelper.USECASE_ELLIPSE_FORM_NAME_OUTSIDE) ) {
					return new EllipseAnchorMngr(this);
				} 
			}
		} 
		return super.createAnchorManager();
	}

	/**
	 * Returns the Name Label.
	 * @return Label the label where the name is displayed
	 */
	protected Label getNameLabel() {
		IFigure fig = this.getFigure();

		if (fig instanceof IconFigure) {
			return (Label) fig;
		} else if (fig instanceof NodeFigure) {
			return ((NodeFigure) fig).getNameLabel();
		} else {
			Debug.error(this, "UseCaseEditPart::getNameLabel() : Unknown Figure type.", channel);
			return null;
		}
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.editpart.GraphNodeWithSemanticEditPart#getUmlElement()
	 */
	@Override
	public UseCase getUmlElement() {
		Element e = ((Uml1SemanticModelBridge) getGraphNode().getSemanticModel()).getElement();
		// Case of element import
		if (e instanceof ElementImport) {
			return (UseCase) ((ElementImport) e).getImportedElement();
		}
		// This is a simple Use case
		return (UseCase) e;
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.editpart.GraphNodeEditPart#installActionHandle()
	 */
	protected void installActionHandle() {
		// Install mouse hover
		ActionHandleManager actionMngr = new ActionHandleManager(this);
		// Create a toolbar of actions.
		try {
			PaletteDrawer entries = new PaletteDrawer("creation tool");
			// Add shortcuts on use case figure for creating a association with
			// an actor,
			// an extend or include with a use case
			entries.add(PapyrusPlugin.creationToolFactory
					.getConnectionCreationToolEntry(
							PapyrusMessages.AddAssociationCommand_Label,
							PapyrusMessages.AddAssociationCommand_Description,
							Association.class, null));
			entries
					.add(PapyrusPlugin.creationToolFactory
							.getConnectionCreationToolEntry(
									PapyrusMessages.AddGeneralizationCommand_Label,
									PapyrusMessages.AddGeneralizationCommand_Description,
									Generalization.class, null));
			entries.add(PapyrusPlugin.creationToolFactory
					.getConnectionCreationToolEntry(
							PapyrusMessages.AddExtendCommand_Label,
							PapyrusMessages.AddExtendCommand_Description,
							Extend.class, null));
			entries.add(PapyrusPlugin.creationToolFactory
					.getConnectionCreationToolEntry(
							PapyrusMessages.AddIncludeCommand_Label,
							PapyrusMessages.AddIncludeCommand_Description,
							Include.class, null));

			actionMngr.addTool(entries, PositionConstants.EAST, false, 5);
		} catch (Exception e) {
			Debug.log(e);
		}
	}

	/**
	 * the direct edit mechanism requires the edit part to handle the direct
	 * edit request.
	 * @param req the request that called the direct edit.
	 */
	public void performDirectEdit(Request req) {
		initLabelEditManager();
		manager.show();
	}

	/* (non-Javadoc)
	 * @see org.eclipse.gef.editparts.AbstractEditPart#performRequest(org.eclipse.gef.Request)
	 */
	@Override
	public void performRequest(Request request) {
		if (RequestConstants.REQ_DIRECT_EDIT.equals(request.getType())) {
			performDirectEdit(request);
		}
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.editpart.GraphNodeWithSemanticEditPart#refreshVisuals()
	 */
	@Override
	protected void refreshVisuals() {
		Debug.debug(this, "refreshVisuals()", channel);

		Dimension size = getGraphNode().getSize();
		Point p = getGraphNode().getPosition();
		Rectangle r = new Rectangle(p, size);		

		IFigure fig = getFigure();

		// If fig is a stereotype figure
		if (fig instanceof IconFigure) {
			IconFigure icf = (IconFigure) getFigure();
			icf.setName(getUmlElement().getName());
		} else {
			org.eclipse.uml2.uml.UseCase currentUseCase = getUmlElement();
			com.cea.papyrus.umlutils.Element currentElement = new com.cea.papyrus.umlutils.Element(getUmlElement());

			if (fig instanceof BasicUseCaseClassifierFigure) {
				((BasicUseCaseClassifierFigure) fig).setBorderRequired(hasExtensionPoints());
			}
			((NodeFigure) fig).refreshColors(getGraphNode());
			((NodeFigure) fig).refreshPresentation(getPresentation(), currentUseCase, getGraphNode());

			boolean hasIcon = currentElement.hasIcon();
			boolean hasShape = currentElement.hasShape();

			((NodeFigure) fig).refreshStereotypes(((NodeFigure) fig).stereotypesLabel(this), getPresentation(), hasIcon,hasShape);
			((NodeFigure) fig).setName(currentUseCase.getName());
			// qualifiedName
			GraphNode gp = (GraphNode) getGraphNode().getContainer();
			((NodeFigure) fig).refreshQualifiedName(gp, getGraphNode(),	getUmlElement());
		}
		
		((GraphicalEditPart) getParent()).setLayoutConstraint(this, fig, r);
	}

	/* (non-Javadoc)
	 * @see org.eclipse.gef.editparts.AbstractEditPart#createChild(java.lang.Object)
	 */
	@Override
	protected EditPart createChild(Object model) {
		Debug.debug(this, "createChild()", channel);
		if (model instanceof GraphNode) {
			EditPart editpart = null;
			// First graphNode, container of extension point
			if (((GraphNode) model) == ((GraphNode) getGraphNode()
					.getContained().get(0))) {
				editpart = new ExtensionPointListEditPart((GraphNode) model,
						modelManager);
			} else {
				throw new RuntimeException("unknown model list");
			}
			editpart.setModel(model);
			return editpart;
		} // Else
		return super.createChild(model);
	}

	
	/* (non-Javadoc)
	 * @see org.eclipse.gef.editparts.AbstractGraphicalEditPart#getContentPane()
	 */
	@Override
	public IFigure getContentPane() {
		if (getFigure() instanceof UseCaseEllipseGeneralFigure) {
			return ((UseCaseEllipseGeneralFigure) getFigure()).getContentPane();
		} else if ((getFigure() instanceof UseCaseClassifierFormFigure)) {
			return ((UseCaseClassifierFormFigure) getFigure()).getContentPane();
		} else {
			// Figure is a shape
			return getFigure();
		}
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.editpart.ContainerWithSemanticEditPart#createEditPolicies()
	 */
	@Override
	protected void createEditPolicies() {
		super.createEditPolicies();
		installEditPolicy(EditPolicy.GRAPHICAL_NODE_ROLE, new com.cea.papyrus.diagram.usecase.editpart.GraphNodeEditPolicy() );
		// Add shortcuts
		installActionHandle();
	}

	/**
	 * Init the label direct edit manager.
	 * This method should be called when a Direct Edit request is performed.
	 */
	protected void initLabelEditManager() {
		if(manager==null) {
			// locator for the cell editor (on the name label)
			CellEditorLocator locator = new LabelCellEditorLocator(getNameLabel());
	
			// defining how properties are modified in the model
			PropertyAccessor accessor = new PropertyAccessor() {
				public String getPropertyName() {
					return "Name";
				}
	
				public String getValue() {
					if(isElementImport()){
						return getElementImport().getAlias();
					}
					return getUmlElement().getName();
				}
				public void setValue(String value) {
					if(isElementImport()){
						getElementImport().setAlias(value);
					}
					else{getUmlElement().setName(value);}  
				}
			};
			// defines the controler: link between model and view
			ILabelControler labelControler = new LabelControler(getNameLabel(), accessor);
	
			// creates the editing manager.
			manager = new LabelDirectEditManager(this, locator, labelControler);
		}
	}
	
	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.editpart.GraphNodeEditPart#changeFigureToStereotypeFigure()
	 */
	@Override
	public void changeFigureToStereotypeFigure() {
		super.changeFigureToStereotypeFigure();
		// Force node name refresh 
		getGraphNode().setBackgroundColor(getGraphNode().getBackgroundColor());		
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.common.editpart.GraphNodeEditPart#changeEditPartFigure()
	 */
	@Override
	public void changeEditPartFigure() {
		IFigure fparent = figure.getParent();
		if (fparent != null) {	
			// Initialize children list to force refresh on children editparts
			for (int i = 0; i < getChildren().size(); i++) {
				EditPart ep = (EditPart)getChildren().get(i);
				removeChild(ep);
			}
			
			// Unselect editpart
			setFocus(false);
			setSelected(EditPart.SELECTED_NONE);
					
			// Unregister current figure
			unregisterVisuals();
			
			// Create new figure
			IFigure newFigure = createFigure();
			
			// Remove current figure and add new one in the figure tree
			fparent.remove(figure);
			fparent.add(newFigure);
			
			// Set figure and register it
			setFigure(newFigure);		
			registerVisuals();

			children = new ArrayList();	
			
			// Re-install all edit policies instead of action handles only
			anchorMngr = null;
			createEditPolicies();
			
			// Refresh editpart
			refresh();
		}
	}
}